//
// Created by 86135 on 2025-06-17.
//

#include "ast_printer.h"
#include <stdio.h>

const char* nodeTypeToString(ASTNodeType type) {
    static const char* names[] = {
            "PROGRAM", "BLOCK", "IF", "EXPR_STMT", "BINARY_EXPR",
            "LITERAL", "EMPTY_STMT", "IDENTIFIER", "ASSIGNMENT",
            "LOGICAL_OR", "LOGICAL_AND", "EQUALITY", "RELATIONAL",
            "WHILE", "FOR", "BREAK", "CONTINUE",
            "RETURN", "DECLARATION",
            "PARAMETER", "TYPE_SPECIFIER", "ARGUMENT_LIST"
            //todo..
    };
    return names[type];
}

// 扩展运算符映射
const char* operatorToString(int op) {
    switch (op) {
        case TOKEN_PLUS: return "+";
        case TOKEN_MINUS: return "-";
        case TOKEN_STAR: return "*";
        case TOKEN_SLASH: return "/";
        case TOKEN_PERCENT: return "%";
        case TOKEN_EQUAL: return "=";
        case TOKEN_PIPE_PIPE: return "||";
        case TOKEN_AMPER_AMPER: return "&&";
        case TOKEN_EQUAL_EQUAL: return "==";
        case TOKEN_BANG_EQUAL: return "!=";
        case TOKEN_LESS: return "<";
        case TOKEN_LESS_EQUAL: return "<=";
        case TOKEN_GREATER: return ">";
        case TOKEN_GREATER_EQUAL: return ">=";

            // todo..
        default: {
            static char buf[16];
            snprintf(buf, sizeof(buf), "OP%d", op);
            return buf;
        }
    }
}

void printIndent(int level) {
    for (int i = 0; i < level; i++) {
        printf("  ");
    }
}

void printAST(ASTNode* node, int indent) {
    if (node == NULL) {
        printIndent(indent);
        printf("NULL\n");
        return;
    }

    printIndent(indent);
    printf("%s (line: %d)\n", nodeTypeToString(node->type), node->line);

    switch (node->type) {
        case AST_PROGRAM:
            printIndent(indent);
            printf("  Declarations: %d\n", node->data.program.declCount);
            for (int i = 0; i < node->childCount; i++) {
                printAST(node->children[i], indent + 1);
            }
            break;

        case AST_BLOCK:
            printIndent(indent);
            printf("  Statements: %d\n", node->data.block.stmtCount);
            for (int i = 0; i < node->childCount; i++) {
                printAST(node->children[i], indent + 1);
            }
            break;

        case AST_IF:
            printIndent(indent);
            printf("  Condition:\n");
            printAST(node->data.ifStmt.condition, indent + 2);

            printIndent(indent);
            printf("  If Body:\n");
            printAST(node->data.ifStmt.ifBody, indent + 2);

            printIndent(indent);
            printf("  Else Body:\n");
            printAST(node->data.ifStmt.elseBody, indent + 2);
            break;

        case AST_EXPR_STMT:
            printIndent(indent);
            printf("  Expression:\n");
            printAST(node->data.exprStmt.expr, indent + 1);
            break;

        case AST_BINARY_EXPR:
            printIndent(indent);
            printf("  Operator: %s\n", operatorToString(node->data.binaryExpr.operator));

            printIndent(indent);
            printf("  Left:\n");
            printAST(node->data.binaryExpr.left, indent + 1);

            printIndent(indent);
            printf("  Right:\n");
            printAST(node->data.binaryExpr.right, indent + 1);
            break;

        case AST_LITERAL:
            printIndent(indent);
            printf("  Value: %f\n", node->data.literal.value);
            break;

        case AST_EMPTY_STMT:
            printIndent(indent);
            printf("  (Empty Statement)\n");
            break;

        case AST_IDENTIFIER:
            printIndent(indent);
            printf("  Identifier: %s\n", node->data.identifier.name);
            break;

            //v4
        case AST_ASSIGNMENT:
            printIndent(indent);
            printf("  Operator: %s\n", operatorToString(node->data.assignment.operator));
            printAST(node->data.assignment.left, indent + 1);
            printAST(node->data.assignment.right, indent + 1);
            break;

        case AST_LOGICAL_OR:
            printIndent(indent);
            printf("  Operator: %s\n", operatorToString(node->data.logicalOr.operator));
            printAST(node->data.logicalOr.left, indent + 1);
            printAST(node->data.logicalOr.right, indent + 1);
            break;

        case AST_LOGICAL_AND:
            printIndent(indent);
            printf("  Operator: %s\n", operatorToString(node->data.logicalAnd.operator));
            printAST(node->data.logicalAnd.left, indent + 1);
            printAST(node->data.logicalAnd.right, indent + 1);
            break;

        case AST_EQUALITY:
            printIndent(indent);
            printf("  Operator: %s\n", operatorToString(node->data.equality.operator));
            printAST(node->data.equality.left, indent + 1);
            printAST(node->data.equality.right, indent + 1);
            break;

        case AST_RELATIONAL:
            printIndent(indent);
            printf("  Operator: %s\n", operatorToString(node->data.relational.operator));
            printAST(node->data.relational.left, indent + 1);
            printAST(node->data.relational.right, indent + 1);
            break;
            //v5
        case AST_WHILE:
            // 删除了额外的节点类型打印，直接打印内容
            printIndent(indent);
            printf("  Condition:\n");
            printAST(node->data.whileStmt.condition, indent+2);
            printIndent(indent);
            printf("  Body:\n");
            printAST(node->data.whileStmt.body, indent+2);
            break;

        case AST_FOR:
            printIndent(indent);
            printf("  Init:\n");
            printAST(node->data.forStmt.init, indent+2);
            printIndent(indent);
            printf("  Condition:\n");
            printAST(node->data.forStmt.condition, indent+2);
            printIndent(indent);
            printf("  Update:\n");
            printAST(node->data.forStmt.update, indent+2);
            printIndent(indent);
            printf("  Body:\n");
            printAST(node->data.forStmt.body, indent+2);
            break;

        case AST_BREAK:
            break;      // 因为无孩子结点，所以无需做任何处理，在前面已经打印过自己了，不需要再打印。

        case AST_CONTINUE:
            break;

        case AST_RETURN:
            if (node->data.returnStmt.expr) {
                printIndent(indent);
                printf("  Expression:\n");
                printAST(node->data.returnStmt.expr, indent + 2);
            }
            break;

        case AST_DECLARATION:
            printIndent(indent);
            printf("  Type:\n");
            printAST(node->data.declaration.typeSpec, indent + 2);
            printIndent(indent);
            printf("  Identifier:\n");
            printAST(node->data.declaration.identifier, indent + 2);
            if (node->data.declaration.initExpr) {
                printIndent(indent);
                printf("  Initializer:\n");
                printAST(node->data.declaration.initExpr, indent + 2);
            }
            break;

        case AST_PARAMETER:
            printIndent(indent);
            printf("  Type:\n");
            printAST(node->data.parameter.typeSpec, indent + 2);
            printIndent(indent);
            printf("  Identifier:\n");
            printAST(node->data.parameter.identifier, indent + 2);
            break;

        case AST_TYPE_SPECIFIER:
            printIndent(indent);
            switch (node->data.typeSpecifier.type) {
                case TOKEN_INT: printf("  int\n"); break;
                case TOKEN_CHAR: printf("  char\n"); break;
                case TOKEN_FLOAT: printf("  float\n"); break;
                case TOKEN_DOUBLE: printf("  double\n"); break;
                case TOKEN_VOID: printf("  void\n"); break;
                default: printf("  unknown\n");
            }
            break;

        case AST_ARGUMENT_LIST:
            printIndent(indent);
            for (int i = 0; i < node->childCount; i++) {
                printAST(node->children[i], indent + 1);
            }
            break;
            // todo..
        default:
            printIndent(indent);
            printf("  Unhandled node type\n");
    }
}